import { deepCopy } from "@/package/treeMethods/copy";

/**
 * @desc 开发中树结构方法封装
 * @param {Array|Object} data - 数据
 * @param {boolean} isTree - 是否为树结构
 * @param {Object} configuration - 树结构配置 { label: 显示的名字, key: 实际的值, isLeaf: 是否根节点, children: 子树 }
 * @return {*}
 * */
function TreeMethods<Config, Node>(data = [], isTree: boolean = false, configuration: Config) {
  configuration = {
    ...{
      key: "id",
      data: "data",
      parentKey: "parentId",
      leafKey: "leaf",
      children: "children",
      rootId: null
    },
    // @ts-ignore
    ...configuration,
  };

  this.tree = null;
  this.array = null;
  this.source = data;
  this.isTree = isTree;
  this.configuration = configuration;

  // 初始化
  this._constructor();
}

/** @desc 初始化 */
Tree.prototype._constructor = function(): void {
  let data = this.source;
  // 如果data不是对象, 并且也不是数组, 就报错
  if (typeof data !== "object" && !Array.isArray(data))
    throw new Error(`Expected type is array, get the ${typeof data}`);
  // 如果data不是数组 就包上一层
  if (!Array.isArray(data)) data = [data];
  if (this.isTree) {
    this.tree = data;
    this.array = this.generateArray();
  } else {
    const { rootId } = this.configuration;
    if (!rootId)
      throw new Error(`源数据不为树结构, 必须在configuration中添加rootId属性`);
    this.array = data;
    this.tree = this.generateTree(data, rootId);
  }
};

/**
 * @desc 将一维数组转换为树结构
 * @param {Array} data - 源数据
 * @param {number|string} rootId - 根节点的唯一标识符
 * @param {string} key - 唯一标识符的名字
 * @param {string} parentKey - 指向父级唯一标识符的名字
 * @param {string} children - 生成数据的子节点属性名
 * @param {string} leafKey - 生成数据是否为叶子节点的属性名
 * @return {Array|*} 返回树结构 或者是 原数据
 */
Tree.prototype.generateTree = function (
  data: Array<any>,
  rootId = this.configuration.rootId,
  key = this.configuration.key,
  parentKey = this.configuration.parentKey,
  children = this.configuration.children,
  leafKey = this.configuration.leafKey
) : Array<Node> | Error {
  try {
    const cloneData = data.map((item) => deepCopy(item));

    const treeData = cloneData.filter((father) => {
      // 直接筛选出子项
      const branchArr = cloneData.filter((child) => {
        return father[key] === child[parentKey];
      });

      // 只有当父节点的children属性为空或者长度小于1(也就是没有)的时候, 才会重新修改它
      if (!father[children] || father[children].length < 1) {
        // 因为father肯定是一个引用属性, 所以在这个地方给father添加children属性, 那么cloneData里面的值也会变, 同样的因为father是一个引用属性, 所以当cloneData里面对应的项变化后, father里面的children属性也会随之变化
        father[leafKey] = true;
        father[children] = [];
        if (branchArr.length > 0) {
          father[children] = branchArr;
          father[leafKey] = false;
        }
      }
      // 只返回根节点
      return father[key] === rootId;
    });

    return treeData.length > 0 ? treeData : data;
  } catch (error) {
    return error;
  }
};

/**
 * @desc 将树结构转换为一维数组 - 深度优先
 * @return {Array} 返回一维数组
 * */
Tree.prototype.generateArray = function(): Array<Node> {
  const array: Array<Node> = [];
  const recursion = (data: Node & any) => {
    data = deepCopy(data);
    if (!data[this.configuration.leafKey]) {
      const children = data[this.configuration.children];
      for (let i = 0; i < children.length; i++) {
        let current = deepCopy(children[i]);
        recursion(current); // 处理子节点
      }
      delete data.children;
      array.push(data); // 处理完子节点后, 再把自己扔进去
    } else {
      // 如果根节点是叶子节点的话, 就直接把自己扔进去
      delete data.children;
      array.push(data);
    }
  };
  recursion(this.tree[0]);

  return array;
};

/**
 * @desc 根据key查询一个节点
 * @param {number|string} key
 * @return {Object} 返回节点信息
 * */
Tree.prototype.findOneNode = function(key: number): Node {
  return this.array.find((item: Node & any) => item[this.configuration.key] === key);
};

/**
 * @desc 根据key查询当前节点的所有直系父节点(包含当前节点以及子节点) - 深度优先
 * @param {number|string} key
 * @return {Array} 返回树结构
 * */
Tree.prototype.findParentNode = function(key: number): Array<Node> | null {
  const {
    key: interiorKey,
    children: childKey,
    parentKey: parentKey,
  } = this.configuration;
  const stock = [];
  let current = null;
  // 1.递归找到要找的这个节点(带children的)
  const recursion = (node: Node & any) => {
    if (node[interiorKey] === key) return (current = node);
    const children = node[childKey];
    for (let i = 0; i < children.length; i++) {
      recursion(children[i]);
    }
  };
  recursion(this.tree[0]);

  if (current) {
    // 如果找到了对应的数据, 先把他push进去
    stock.push(current);
    const clone = this.array.map((item: Node) => deepCopy(item));
    let parentId: number = current[parentKey];
    let parent = null;
    // 一级一级不停的找parentId, 如果找到了就push到stack里面
    while (parent !== undefined) {
      parent = clone.find((item: Node & any) => {
        if (item[interiorKey] === parentId) {
          parentId = item[parentKey];
          return true;
        }
      });
      parent && stock.push(parent);
    }
    const rootId = stock[stock.length - 1][interiorKey]; // 取出根节点id(最后一个就是根节点)
    return this.generateTree(stock, rootId);
  }

  return null;
};

/**
 * @desc 根据key查询当前节点的子节点(包含当前节点) - 深度优先
 * @param {number|string} key
 * @return {Array} 返回树结构
 * */
Tree.prototype.findSubTree = function(key: number): Array<Node | null> {
  let result: Node | null = null;
  const recursion = (subTree: Node & any) => {
    if (subTree[this.configuration.key] === key) {
      result = subTree;
      return false;
    }
    const children: Array<Node & null> = subTree[this.configuration.children];
    // 如果子节点大于0, 就先循环子节点
    if (children.length > 0) {
      for (let i = 0; i < children.length; i++) {
        const current: Node & null = children[i];
        if (current[this.configuration.key] === key) {
          result = current;
          return false;
        }
        recursion(current);
      }
    }
  };
  recursion(this.tree[0]);
  return [result];
};

/**
 * @desc 获取树中的数据
 * @return {Array} 返回树中的数据
 * */
Tree.prototype.getTree = function(): Array<Node> {
  return this.tree;
};

/**
 * @desc 获取原数据
 * @return {*} 返回原数据
 * */
Tree.prototype.getSource = function(): Array<Node> {
  return this.source;
};

/**
 * @desc 获取一维数组
 * @return {Array} 返回一维数组
 * */
Tree.prototype.getArray = function(): Array<Node> {
  return this.array;
};

/**
 * @desc 获取根节点
 * @return {Object} 返回根节点
 * */
Tree.prototype.getRoot = function(): Node {
  const copy = deepCopy(this.tree)[0];
  delete copy[this.configuration.children];
  return copy;
};

/**
 * @desc 获取树的最大深度
 * @return {number} 返回树的最大深度
 * */
Tree.prototype.getDepth = function(): number {
  const childKey = this.configuration.children;
  let depth = 0;

  const recursion = function(subTree: Node & any, level: number) {
    for (let i = 0; i < subTree.length; i++) {
      if (level > depth) {
        depth = level;
      }
      if (subTree[i][childKey].length > 0) {
        recursion(subTree[i][childKey], level + 1);
      }
    }
  };
  recursion(this.tree, 1);
  return depth;
};

/**
 * @desc 修改节点
 * @param {string|number} key - 要修改节点的唯一标识
 * @param {*} data - 新数据
 * @return {boolean} true:成功 false:失败
 * */
Tree.prototype.update = function(key: number, data: number): boolean {
  const primaryKey = this.configuration.key;
  const valueKey = this.configuration.data;
  const childKey = this.configuration.children;
  // 如果有修改的节点
  if (this.array.find((item: Node & any) => item[primaryKey] === key)) {
    this.array.forEach(
      (item: Node & any) => item[primaryKey] === key && (item[valueKey] = data)
    );
    const recursion = (subTree: Node & any) => {
      if (subTree[primaryKey] === key) {
        subTree[valueKey] = data;
      } else {
        for (let i = 0; i < subTree[childKey].length; i++) {
          recursion(subTree[childKey][i]);
        }
      }
    };
    recursion(this.tree[0]);
    return true;
  } else {
    return false;
  }
};

/**
 * @desc 删除节点
 * @param {string|number} key
 * @return {Object} 返回被删除的节点
 * */
Tree.prototype.remove = function(key: number): Node {
  if (this.array.length > 2 && key === this.configuration.rootId)
    throw new Error("不能直接删除根节点");
  const primaryKey = this.configuration.key;
  const childKey = this.configuration.children;
  const index = this.array.findIndex((item: Node & any) => item[primaryKey] === key);
  const removeItem = this.array.splice(index, 1);

  // 递归删除一维数组
  const recursionRemove = (parentId: number) => {
    this.array.forEach((item: Node & any, i: number) => {
      if (item.parentId === parentId) {
        const removeChild = this.array.splice(i, 1);
        recursionRemove(removeChild[0].id);
      }
    });
  };

  recursionRemove(removeItem[0].id);

  // 递归删除树结构
  const recursion = (subTree: Node & any) => {
    const children = subTree[childKey];
    for (let i = 0; i < children.length; i++) {
      if (children[i][primaryKey] === key) {
        subTree[childKey].splice(i, 1);
      } else {
        recursion(children[i]);
      }
    }
  };
  recursion(this.tree[0]);
  return removeItem[0];
};

/**
 * @desc 给某个节点追加一个子节点
 * @param {string|number} key - 父节点
 * @param {Object} node - 追加的节点
 * @return {boolean} true:成功 false:失败
 * */
Tree.prototype.append = function(key: number, node: Node): boolean {
  const childKey = this.configuration.children;
  const primaryKey = this.configuration.key;

  const recursion = (subTree: Node & any) => {
    if (subTree[primaryKey] === key) {
      subTree[childKey].push(node);
      return true;
    }
    const children = subTree[childKey];
    for (let i = 0; i < children.length; i++) {
      const state = recursion(children[i]);
      if (state) return true;
    }
  };

  const state = recursion(this.tree[0]);
  if (state) {
    this.array.push(node);
    return true;
  }
  return false;
};

/**
 * @desc 在某个节点前面增加一个节点
 * @param {string|number} key - 在哪个节点之前
 * @param {Object} node - 追加的节点
 * @return {boolean} true:成功 false:失败
 * */
Tree.prototype.insertBefore = function(key: number, node: Node): boolean {
  const childKey = this.configuration.children;
  const primaryKey = this.configuration.key;

  const recursion = (subTree: Node & any) => {
    const children = subTree[childKey];
    for (let i = 0; i < children.length; i++) {
      if (children[i][primaryKey] === key) {
        subTree[childKey].splice(i, 0, node);
        return true;
      } else {
        const state = recursion(children[i]);
        if (state) return true;
      }
    }
  };

  const state = recursion(this.tree[0]);

  if (state) {
    this.array.push(node);
    return true;
  }
  return false;
};

/**
 * @desc 在某个节点后面增加一个节点
 * @param {string|number} key - 在哪个节点之后
 * @param {Object} node - 增加的节点
 * @return {boolean} true:成功 false:失败
 * */
Tree.prototype.insertAfter = function(key: number, node: Node): boolean {
  const childKey = this.configuration.children;
  const primaryKey = this.configuration.key;

  const recursion = (subTree: Node & any) => {
    const children = subTree[childKey];
    for (let i = 0; i < children.length; i++) {
      if (children[i][primaryKey] === key) {
        subTree[childKey].splice(i + 1, 0, node);
        return true;
      } else {
        const state = recursion(children[i]);
        if (state) return true;
      }
    }
  };

  const state = recursion(this.tree[0]);

  if (state) {
    this.array.push(node);
    return true;
  }
  return false;
};

export { TreeMethods };
